package io.shuttle.mbe.api

import io.shuttle.mbe.api.annotation.ChromeMinVersion
import io.shuttle.mbe.api.annotation.LowerEqMV3
import io.shuttle.mbe.api.annotation.PromiseStyleMinVersion
import io.shuttle.mbe.api.types.StringOrStringArr
import io.shuttle.mbe.api.types.StringOrStringOrAnyArr
import io.shuttle.mbe.api.types.Value1Function
import io.shuttle.mbe.api.types.Value2Function
import io.shuttle.mbe.api.types.VoidFunction
import io.shuttle.mbe.core.Promise

////////////////////
// Storage
////////////////////
/**
 * Use the `Browser.storage` API to store, retrieve, and track changes to user data.
 *
 * Permissions: "storage"
 */
interface Storage {

    // Items in the local storage area are local to each machine.
    val local: LocalStroageArea

    interface LocalStroageArea : StorageArea {
        // The maximum amount (in bytes) of data that can be stored in local storage, as measured by the JSON stringification of every value plus every key's length. This value will be ignored if the extension has the unlimitedStorage permission. Updates that would cause this limit to be exceeded fail immediately and set runtime.lastError when using a callback, or a rejected Promise if using async/await.
        // default is 10485760
        val QUOTA_BYTES: Int
    }

    interface SessionStorageArea : StorageArea {
        /** The maximum amount (in bytes) of data that can be stored in memory, as measured by estimating the dynamically allocated memory usage of every value and key. Updates that would cause this limit to be exceeded fail immediately and set runtime.lastError. */
        val QUOTA_BYTES: Int
    }

    // Items in the managed storage area are set by an enterprise policy configured by the domain administrator, and are read-only for the extension; trying to modify this namespace results in an error. For information on configuring a policy, see Manifest for storage areas.
    val managed: StorageArea

    // Items in the sync storage area are synced using Chrome Sync.
    val sync: SyncStorageArea

    /**
     * Items in the session storage area are stored in-memory and will not be persisted to disk.
     * @since Chrome 102
     */
    @ChromeMinVersion(102)
    val session: SessionStorageArea

    interface SyncStorageArea : StorageArea {
        // The maximum number of items that can be stored in sync storage. Updates that would cause this limit to be exceeded will fail immediately and set runtime.lastError when using a callback, or when a Promise is rejected.
        // default is 512
        val MAX_ITEMS: Int

        @Deprecated(
            "Deprecated",
            replaceWith = ReplaceWith("The storage.sync API no longer has a sustained write operation quota.")
        )
        // default is 1000000
        val MAX_SUSTAINED_WRITE_OPERATIONS_PER_MINUTE: Int

        // The maximum number of set, remove, or clear operations that can be performed each hour. This is 1 every 2 seconds, a lower ceiling than the short term higher writes-per-minute limit.
        //
        //Updates that would cause this limit to be exceeded fail immediately and set runtime.lastError when using a callback, or when a Promise is rejected.
        // default is 1800
        val MAX_WRITE_OPERATIONS_PER_HOUR: Int

        // The maximum number of set, remove, or clear operations that can be performed each minute. This is 2 per second, providing higher throughput than writes-per-hour over a shorter period of time.
        //
        //Updates that would cause this limit to be exceeded fail immediately and set runtime.lastError when using a callback, or when a Promise is rejected.
        // default is 120
        val MAX_WRITE_OPERATIONS_PER_MINUTE: Int

        // The maximum total amount (in bytes) of data that can be stored in sync storage, as measured by the JSON stringification of every value plus every key's length. Updates that would cause this limit to be exceeded fail immediately and set runtime.lastError when using a callback, or when a Promise is rejected.
        // default is 102400
        val QUOTA_BYTES: Int

        // The maximum size (in bytes) of each individual item in sync storage, as measured by the JSON stringification of its value plus its key length. Updates containing items larger than this limit will fail immediately and set runtime.lastError when using a callback, or when a Promise is rejected.
        // default is 8192
        val QUOTA_BYTES_PER_ITEM: Int
    }

    // Fired when one or more items change.
    /**
     * @param changes
     * @param areaName
     */
    val onChanged: Events.Event<Value2Function<StorageChangeInfo, AreaName>>

    data class StorageChangeInfo(
        var customFields: Map<String, StorageChange>
    )

    // The storage area's access level.
    @ChromeMinVersion(102)
    enum class AccessLevel {
        // Specifies contexts originating from the extension itself.
        TRUSTED_CONTEXTS,

        // Specifies contexts originating from outside the extension.
        TRUSTED_AND_UNTRUSTED_CONTEXTS
    }


    interface StorageArea {
        // Fired when one or more items change.
        @ChromeMinVersion(73)
        val onChanged: Events.Event<Value1Function<Any>>

        // Removes all items from storage.
        @PromiseStyleMinVersion(95)
        @LowerEqMV3
        fun clear(callback: VoidFunction? = null): Promise<Void>

        // Gets one or more items from storage.
        @PromiseStyleMinVersion(95)
        @LowerEqMV3
        fun get(
            // A single key to get, list of keys to get, or a dictionary specifying default values (see description of the object). An empty list or object will return an empty result object. Pass in null to get the entire contents of storage.
            keys: StringOrStringOrAnyArr,
            // items - Object with items in their key-value mappings.
            callback: Value1Function<Any>? = null
        ): Promise<Any>

        // Gets the amount of space (in bytes) being used by one or more items.
        @PromiseStyleMinVersion(95)
        @LowerEqMV3
        fun getBytesInUse(
            // A single key or list of keys to get the total usage for. An empty list will return 0. Pass in null to get the total usage of all of storage.
            keys: StringOrStringArr?,
            // bytesInUser - Amount of space being used in storage, in bytes.
            callback: Value1Function<Number>? = null
        ): Promise<Number>

        // Gets all keys from storage.
        @ChromeMinVersion(130)
        @PromiseStyleMinVersion(95)
        fun getKeys(
            // keys - Array with keys read from storage.
            callback: Value1Function<List<String>>? = null
        ): Promise<List<String>>

        // Removes one or more items from storage.
        @PromiseStyleMinVersion(95)
        @LowerEqMV3
        fun remove(
            // A single key or a list of keys for items to remove.
            keys: StringOrStringArr, callback: VoidFunction? = null
        ): Promise<Void>

        // Sets multiple items.
        @PromiseStyleMinVersion(95)
        @LowerEqMV3
        fun set(
            // An object which gives each key/value pair to update storage with. Any other key/value pairs in storage will not be affected.
            //
            //Primitive values such as numbers will serialize as expected. Values with a typeof "object" and "function" will typically serialize to {}, with the exception of Array (serializes as expected), Date, and Regex (serialize using their String representation).
            items: Any, callback: VoidFunction? = null
        ): Promise<Void>

        // Sets the desired access level for the storage area. By default, session storage is restricted to trusted contexts (extension pages and service workers), while managed, local, and sync storage allow access from both trusted and untrusted contexts.
        @ChromeMinVersion(102)
        @PromiseStyleMinVersion(95)
        fun setAccessLevel(
            accessOptions: AccessOptions, callback: VoidFunction? = null
        ): Promise<Void>
    }


    data class StorageChange(
        // The new value of the item, if there is a new value.
        var newValue: Any?,
        // The old value of the item, if there was an old value.
        var oldValue: Any?,
    )

    data class AccessOptions(
        var accessLevel: AccessLevel,
    )

    enum class AreaName {
        sync,
        local,
        managed,
        session
    }
}